本篇是實作常用的 AWS ECR 服務之 Terraform 模組,並且會使用到 YAML 資料結構來定義模組的內容,完整的專案程式碼分享在我的 Github 上。
my_ecr
的放置位置 modules/my_ecr
:├── configs
│ ├── cloudfront
│ │ └── distributions.yaml
│ ├── cloudwatch
│ │ └── loggroups.yaml
│ ├── dynamodb
│ │ └── configurations.yaml
│ ├── ecr
│ │ ├── lifecycle_policy.json
│ │ ├── permission_policy.json
│ │ └── repos.yaml
│ ├── iam
│ │ ├── assume_role_policies
│ │ │ ├── eks-cluster.json
│ │ │ ├── eks-fargate-pod-execution-role.json
│ │ │ └── eks-node-group.json
│ │ ├── iam.yaml
│ │ ├── role_policies
│ │ │ └── eks-cluster-cloudwatch-metrics.json
│ │ └── user_policies
│ │ └── admin_access.json
│ ├── kinesis
│ │ └── streams.yaml
│ ├── kms
│ │ ├── keys.yaml
│ │ └── policies
│ │ └── my-key-policy.json
│ ├── s3
│ │ ├── policies
│ │ │ └── my-bucket.json
│ │ └── s3.yaml
│ ├── subnet
│ │ └── my-subnets.yaml
│ └── vpc
│ └── my-vpcs.yaml
├── example.tfvars
├── locals.tf
├── main.tf
├── modules
│ ├── my_aws_load_balancer_controller
│ ├── my_cloudfront
│ ├── my_cloudwatch
│ ├── my_dynamodb
│ ├── my_ecr
│ │ ├── ecr_lifecycle_policy.tf
│ │ ├── ecr_repository.tf
│ │ ├── ecr_repository_policy.tf
│ │ ├── provider.tf
│ │ └── variables.tf
│ ├── my_eips
│ ├── my_eks
│ ├── my_eventbridge
│ ├── my_iam
│ ├── my_igw
│ ├── my_instances
│ ├── my_karpenter
│ ├── my_kinesis_stream
│ ├── my_kms
│ ├── my_msk
│ ├── my_nacls
│ ├── my_route_tables
│ ├── my_s3
│ ├── my_subnets
│ └── my_vpc
├── my-ingress-controller-values.yaml
├── my-ingress-node-red.yaml
├── packer
│ └── apache-cassandra
└── variables.tf
./configs/ecr/repos.yaml
內容來定義 ECR 需要用建立的資源:repos:
- name: "<REPO_NAME>"
lifecycle: "<LIFECYCLE_JSON_PATH>"
permission: "<PERMISSION_JSON_PATH>"
image_tag_mutable: <true or false>
scan_on_push: <true or false>
my_ecr
模組:./modules/my_ecr/provider.tf
:provider "aws" {
region = var.aws_region
profile = var.aws_profile
}
./modules/my_ecr/variables.tf
:variable "aws_region" {
description = "AWS region"
default = "ap-northeast-1"
}
variable "aws_profile" {
description = "AWS profile"
default = ""
}
variable "project_name" {
type = string
description = "Project name"
default = ""
}
variable "department_name" {
type = string
description = "Department name"
default = "SRE"
}
variable "repo_path" {
description = "repo path"
default = ""
}
locals {
repos = yamldecode(file("${var.repo_path}"))["repos"]
}
resource "aws_ecr_repository" "repos" {
for_each = { for r in local.repos : r.name => r }
encryption_configuration {
encryption_type = "AES256"
}
image_scanning_configuration {
scan_on_push = lookup(each.value, "scan_on_push", "false")
}
image_tag_mutability = lookup(each.value, "image_tag_mutable", true) ? "MUTABLE" : "IMMUTABLE"
name = each.value.name
}
./modules/my_ecr/ecr_lifecycle_policy.tf
:resource "aws_ecr_lifecycle_policy" "repos_lifecycle_policy" {
for_each = { for r in local.repos : r.name => r if r.lifecycle != "" }
policy = file("${each.value.lifecycle}")
repository = each.value.name
depends_on = [
aws_ecr_repository.repos
]
}
./modules/my_ecr/ecr_repository_policy.tf
:resource "aws_ecr_repository_policy" "repos_policy" {
for_each = { for r in local.repos : r.name => r if r.permission != "" }
policy = file("${each.value.permission}")
repository = each.value.name
depends_on = [
aws_ecr_repository.repos
]
}
example.tfvars
:aws_region="ap-northeast-1"
aws_profile="<YOUR_PROFILE>"
project_name="example"
department_name="SRE"
cassandra_root_password="<CASSANDRA_ROOT_PASSWORD>"
main.tf
:terraform {
required_providers {
aws = {
version = "5.15.0"
}
}
backend "s3" {
bucket = "<YOUR_S3_BUCKET_NAME>"
dynamodb_table = "<YOUR_DYNAMODB_TABLE_NAME>"
key = "terraform.tfstate"
region = "ap-northeast-1"
shared_credentials_file = "~/.aws/config"
profile = "<YOUR_PROFILE>"
}
}
其他模組省略...
# ecr
module "ecr" {
aws_profile = var.aws_profile
aws_region = var.aws_region
repo_path = "./configs/ecr/repos.yaml"
source = "./modules/my_ecr"
}
repos:
- name: my-repo
lifecycle: ./configs/ecr/lifecycle_policy.json
permission: ./configs/ecr/permission_policy.json
image_tag_mutable: true
scan_on_push: true
terraform init && terraform plan --out .plan -var-file=example.tfvars
來確認一下結果:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.ecr.aws_ecr_lifecycle_policy.repos_lifecycle_policy["my-repo"] will be created
+ resource "aws_ecr_lifecycle_policy" "repos_lifecycle_policy" {
+ id = (known after apply)
+ policy = jsonencode(
{
+ rules = [
+ {
+ action = {
+ type = "expire"
}
+ description = "Remove untagged images for 7 days"
+ rulePriority = 1
+ selection = {
+ countNumber = 7
+ countType = "sinceImagePushed"
+ countUnit = "days"
+ tagStatus = "untagged"
}
},
]
}
)
+ registry_id = (known after apply)
+ repository = "my-repo"
}
# module.ecr.aws_ecr_repository.repos["my-repo"] will be created
+ resource "aws_ecr_repository" "repos" {
+ arn = (known after apply)
+ id = (known after apply)
+ image_tag_mutability = "MUTABLE"
+ name = "my-repo"
+ registry_id = (known after apply)
+ repository_url = (known after apply)
+ tags_all = (known after apply)
+ encryption_configuration {
+ encryption_type = "AES256"
+ kms_key = (known after apply)
}
+ image_scanning_configuration {
+ scan_on_push = true
}
}
# module.ecr.aws_ecr_repository_policy.repos_policy["my-repo"] will be created
+ resource "aws_ecr_repository_policy" "repos_policy" {
+ id = (known after apply)
+ policy = jsonencode(
{
+ Statement = [
+ {
+ Action = [
+ "ecr:BatchCheckLayerAvailability",
+ "ecr:BatchGetImage",
+ "ecr:CompleteLayerUpload",
+ "ecr:GetDownloadUrlForLayer",
+ "ecr:InitiateLayerUpload",
+ "ecr:PutImage",
+ "ecr:UploadLayerPart",
]
+ Effect = "Allow"
+ Principal = {
+ AWS = "arn:aws:iam::338584095359:root"
}
+ Sid = "AllowPushPull"
},
]
+ Version = "2008-10-17"
}
)
+ registry_id = (known after apply)
+ repository = "my-repo"
}
Plan: 3 to add, 0 to change, 0 to destroy.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Saved the plan to: .plan
To perform exactly these actions, run the following command to apply:
terraform apply ".plan"
Releasing state lock. This may take a few moments...
下一篇文章將會展示實作 AWS Route53 之 Terraform 模組。